﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Linq;
using Obfuscar;

namespace ObfuscarUtility
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            if (Environment.GetCommandLineArgs().Length > 1)
            {
                try
                {
                    lock (this)
                    {
                        string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();
                        Directory.CreateDirectory(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility");
                        this.ConvertPfxFileToSnk(args);
                        this.WriteConfiguration(args);
                        this.ObfustcateOld(args);
                        this.ClearFiles(args);
                    }
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.ToString());
                }
                Application.Current.Shutdown();
            }
        }

        private void RectangleObfuscate_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data is System.Windows.DataObject && ((System.Windows.DataObject)e.Data).ContainsFileDropList())
            {
                e.Effects = DragDropEffects.Copy;
            }
        }

        private void RectangleObfuscate_DragLeave(object sender, DragEventArgs e)
        {

        }

        private void RectangleObfuscate_DragOver(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }

        private void RectangleObfuscate_Drop(object sender, DragEventArgs e)
        {
            if (e.Data is System.Windows.DataObject && ((System.Windows.DataObject)e.Data).ContainsFileDropList())
            {
                foreach (string filePath in ((System.Windows.DataObject)e.Data).GetFileDropList())
                {
                    //this.Title = filePath;
                    string script = "\r\nif /I \"$(ConfigurationName)\" == \"Release\" \"$(" + Assembly.GetExecutingAssembly().GetName().Name + ")\" \"$(TargetPath)\"";
                    XDocument document = XDocument.Load(filePath);
                    XNamespace xNameSP = document.Root.Name.Namespace;
                    XElement keyElement = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "AssemblyOriginatorKeyFile").FirstOrDefault();
                    if (keyElement != null)
                    {
                        script += " \"$(ProjectDir)" + keyElement.Value + "\"";
                    }
                    XElement element = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "PostBuildEvent").FirstOrDefault();
                    if(element==null)
                    {
                        document.Root.Add(new XElement(xNameSP + "PropertyGroup", new XElement(xNameSP + "PostBuildEvent")));
                        element = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "PostBuildEvent").FirstOrDefault();
                    }
                    if (!element.Value.Contains(script))
                    {
                        element.Value += script;
                    }
                    document.Save(filePath);
                    MessageBox.Show(filePath + " has Obfuscated");
                }
            }
        }

        private void RectangleUnObfuscate_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data is System.Windows.DataObject && ((System.Windows.DataObject)e.Data).ContainsFileDropList())
            {
                e.Effects = DragDropEffects.Copy;
            }
        }

        private void RectangleUnObfuscate_DragLeave(object sender, DragEventArgs e)
        {

        }

        private void RectangleUnObfuscate_DragOver(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }

        private void RectangleUnObfuscate_Drop(object sender, DragEventArgs e)
        {
            if (e.Data is System.Windows.DataObject && ((System.Windows.DataObject)e.Data).ContainsFileDropList())
            {
                foreach (string filePath in ((System.Windows.DataObject)e.Data).GetFileDropList())
                {
                    //this.Title = filePath;
                    string script = "if /I \"$(ConfigurationName)\" == \"Release\" \"$(" + Assembly.GetExecutingAssembly().GetName().Name + ")\" \"$(TargetPath)\"";
                    XDocument document = XDocument.Load(filePath);
                    XNamespace xNameSP = document.Root.Name.Namespace;
                    XElement keyElement = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "AssemblyOriginatorKeyFile").FirstOrDefault();
                    if (keyElement != null)
                    {
                        script += " \"$(ProjectDir)" + keyElement.Value + "\"";
                    }
                    XElement element = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "PostBuildEvent").FirstOrDefault();
                    if (element == null)
                    {
                        document.Root.Add(new XElement(xNameSP + "PropertyGroup", new XElement(xNameSP + "PostBuildEvent")));
                        element = document.Root.Elements(xNameSP + "PropertyGroup").Elements(xNameSP + "PostBuildEvent").FirstOrDefault();
                    }
                    if (element.Value.Contains(script))
                    {
                       element.Value= element.Value.Replace(script, "");
                    }
                    document.Save(filePath);
                    MessageBox.Show(filePath + " has UnObfuscated");
                }
            }
        }


        private void ConvertPfxFileToSnk(string[] args)
        {
            if (args.Length > 1)
            {
                if (args[1].ToLower().EndsWith(".pfx"))
                {
                    Dictionary<string, string> historyDictionary = null;
                    if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\history"))
                    {
                        string text = File.ReadAllText(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\history");
                        byte[] bytes = Convert.FromBase64String(text);
                        try
                        {
                            using (MemoryStream ms = new MemoryStream(bytes))
                            {
                                BinaryFormatter bf = new BinaryFormatter();
                                historyDictionary = (Dictionary<string, string>)bf.Deserialize(ms);
                                if (historyDictionary.ContainsKey(args[1].ToLower()))
                                {
                                    EnterPasswordWindow.ProjectPassword = historyDictionary[args[1].ToLower()];
                                    Trace.WriteLine("Found password in history For " + args[1], Assembly.GetExecutingAssembly().GetName().Name);
                                }
                            }
                        }
                        catch
                        {

                        }
                    }

                    bool correctPassword = false;
                    X509Certificate2 cert = null;
                    do
                    {
                        try
                        {
                            cert = new X509Certificate2(args[1], EnterPasswordWindow.ProjectPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
                            correctPassword = true;
                            if (historyDictionary == null)
                            {
                                historyDictionary = new Dictionary<string, string>();
                            }
                            historyDictionary.Remove(args[1].ToLower());
                            historyDictionary.Add(args[1].ToLower(), EnterPasswordWindow.ProjectPassword);
                            using (MemoryStream ms = new MemoryStream())
                            {
                                BinaryFormatter bf = new BinaryFormatter();
                                bf.Serialize(ms, historyDictionary);
                                string text = Convert.ToBase64String(ms.ToArray());
                                File.WriteAllText(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\history", text);
                                Trace.WriteLine("Write History For file :" + args[1], Assembly.GetExecutingAssembly().GetName().Name);
                            }
                        }
                        catch
                        {
                            Trace.WriteLine("wrong password for pfx file " + args[1], Assembly.GetExecutingAssembly().GetName().Name);
                            EnterPasswordWindow window = new EnterPasswordWindow();
                            window.Title = "Enter password for " + args[1];
                            window.ShowDialog();
                        }
                    }while (!correctPassword);
                    
                    RSACryptoServiceProvider provider = (RSACryptoServiceProvider)cert.PrivateKey;
                    byte[] array = provider.ExportCspBlob(!provider.PublicOnly);
                    using (FileStream fs = new FileStream(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\key.snk", FileMode.Create, FileAccess.Write))
                    {
                        fs.Write(array, 0, array.Length);
                    }
                    args[1] = System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\key.snk";
                    Trace.WriteLine("converted pfx File :" + args[1] + " to snk File", Assembly.GetExecutingAssembly().GetName().Name);
                }
            }
        }
        private void WriteConfiguration(string[] args)
        {
            XDocument document = new XDocument();
            document.Add(new XElement("Obfuscator"));
            document.Root.Add(new XElement("Var",
                                new XAttribute("name", "InPath"),
                                new XAttribute("value", System.IO.Path.GetDirectoryName(args[0]))));
            document.Root.Add(new XElement("Var",
                                new XAttribute("name", "OutPath"),
                                new XAttribute("value", System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility")));
            if (args.Length > 1 && !string.IsNullOrEmpty(args[1]))
            {
                document.Root.Add(new XElement("Var",
                                    new XAttribute("name", "KeyFile"),
                                    new XAttribute("value", args[1])));
            }
            document.Root.Add(new XElement("Module",
                                    new XAttribute("file", "$(InPath)\\" + System.IO.Path.GetFileName(args[0]))));
            if (args[0].ToLower().EndsWith(".dll"))
            {
                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                Assembly assembly = Assembly.Load(File.ReadAllBytes(args[0]));
                var publicTypes = assembly.GetExportedTypes();
                foreach (var item in publicTypes)
                {
                    document.Root.Element("Module").Add(new XElement("SkipType",
                                        new XAttribute("name", item.FullName.Replace("+","/"))));
                    document.Root.Element("Module").Add(new XElement("SkipField",
                                        new XAttribute("type", item.FullName),
                                        new XAttribute("rx", ".*"),
                                        new XAttribute("attrib", "protected")));
                    document.Root.Element("Module").Add(new XElement("SkipProperty",
                                        new XAttribute("type", item.FullName.Replace("+", "/")),
                                        new XAttribute("rx", ".*"),
                                        new XAttribute("attrib", "protected")));
                    document.Root.Element("Module").Add(new XElement("SkipMethod",
                                        new XAttribute("type", item.FullName.Replace("+", "/")),
                                        new XAttribute("rx", ".*"),
                                        new XAttribute("attrib", "protected")));
                    document.Root.Element("Module").Add(new XElement("SkipEvent",
                                        new XAttribute("type", item.FullName.Replace("+", "/")),
                                        new XAttribute("rx", ".*"),
                                        new XAttribute("attrib", "protected")));
                }
            }
            document.Save(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml");
            Trace.WriteLine("Write Configuration For Obfuscations " , Assembly.GetExecutingAssembly().GetName().Name);
            Trace.WriteLine(document.ToString(), Assembly.GetExecutingAssembly().GetName().Name);

        }

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            AssemblyName assemblyName = new AssemblyName(args.Name);
            if (File.Exists(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]) + "\\" + assemblyName.Name + ".dll"))
            {
                return Assembly.LoadFrom(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]) + "\\" + assemblyName.Name + ".dll");
            }
            if (File.Exists(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]) + "\\" + assemblyName.Name + ".exe"))
            {
                return Assembly.LoadFrom(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]) + "\\" + assemblyName.Name + ".exe");
            }
            return null;
        }


        private void ObfustcateOld(string[] args)
        {
            using (Process p = new Process())
            {
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\ObfuscarExe\\Obfuscar.exe";
                p.StartInfo.Arguments = "\"" + System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml\"";
                p.StartInfo.CreateNoWindow = true;
                p.Start();
                using (StreamReader reader = p.StandardOutput)
                {
                    Trace.WriteLine(reader.ReadToEnd(), Assembly.GetExecutingAssembly().GetName().Name);

                }
                p.WaitForExit();
                p.Close();
            }
        }

        private void Obfustcate(string[] args)
        {
            if (args.Length < 1)
            {
                //ShowHelp();
                return ;
            }

            int start = Environment.TickCount;

            try
            {
                Trace.Write("Loading project...", Assembly.GetExecutingAssembly().GetName().Name);
                Obfuscator obfuscator = new Obfuscator(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml");
                Trace.WriteLine("Done.", Assembly.GetExecutingAssembly().GetName().Name);

                // The SemanticAttributes of MethodDefinitions have to be loaded before any fields,properties or events are removed
                obfuscator.LoadMethodSemantics();

                Trace.Write("Renaming:  fields...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameFields();

                Trace.Write("parameters...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameParams();

                Trace.Write("properties...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameProperties();

                Trace.Write("events...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameEvents();

                Trace.Write("methods...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameMethods();

                Trace.Write("types...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.RenameTypes();

                if (obfuscator.Project.Settings.HideStrings)
                {
                    Trace.WriteLine("hiding strings...", Assembly.GetExecutingAssembly().GetName().Name);
                    obfuscator.HideStrings();
                }

                Trace.WriteLine("Done.", Assembly.GetExecutingAssembly().GetName().Name);

                Trace.Write("Saving assemblies...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.SaveAssemblies();
                Trace.WriteLine("Done.", Assembly.GetExecutingAssembly().GetName().Name);

                Trace.Write("Writing log file...", Assembly.GetExecutingAssembly().GetName().Name);
                obfuscator.SaveMapping();
                Trace.WriteLine("Done.", Assembly.GetExecutingAssembly().GetName().Name);

                Trace.WriteLine(string.Format("Completed, {0:f2} secs.", (Environment.TickCount - start) / 1000.0), Assembly.GetExecutingAssembly().GetName().Name);
            }
            catch (ApplicationException e)
            {
                Trace.WriteLine("An error occurred during processing:", Assembly.GetExecutingAssembly().GetName().Name);
                Trace.WriteLine(e.Message, Assembly.GetExecutingAssembly().GetName().Name);
                if (e.InnerException != null)
                    Trace.WriteLine(e.InnerException.Message, Assembly.GetExecutingAssembly().GetName().Name);
                return ;
            }

            return ;
        }
        private void ClearFiles(string[] args)
        {
            if (File.Exists(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0])))
            {
                File.Copy(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]), args[0], true);
                File.Delete(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]));
            }
            if (File.Exists(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml"))
            {
                File.Copy(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml", args[0] + ".xml", true);
                File.Delete(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + System.IO.Path.GetFileName(args[0]) + ".xml");
            }
            if (File.Exists(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + "Mapping.txt"))
            {
                File.Copy(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + "Mapping.txt", System.IO.Path.GetDirectoryName(args[0]) + "\\Mapping.txt", true);
                File.Delete(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\" + "Mapping.txt");
            }
            if (File.Exists(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\key.snk"))
            {
                File.Delete(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility\\key.snk");
            }
            Directory.Delete(System.IO.Path.GetDirectoryName(args[0]) + "\\ObfuscarUtility");

            Trace.WriteLine("clear temp files", Assembly.GetExecutingAssembly().GetName().Name);
        }

    }
}
